#include "parser.h"
#include "y.tab.h"
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>

#define MAX_STRING_SIZE 1024
/*#define DEBUG_LEXIAL_STATE*/

int yyerror(char * a);

static void OpenFile (char * name, PARSER * get);

/* readline intro */

static void readlineFgetc(PARSER * parser) {
  if (parser->yyin == stdin) {
    if (parser->readlineWhere == 0)
      {
	if (! parser->readlineStart)
	  {
	    parser->readlineBuffer = readline ("> ");
	    add_history (parser->readlineBuffer);
	    parser->readlineStart = 1;
	  }
	else{
	  parser->readlineBuffer = readline ("| ");
	  add_history (parser->readlineBuffer);
	}
	parser->readlineWhere = 1;
	if (parser->readlineBuffer[0] == '\0' ) 
	  parser->yychar = EOF;
	else 
	  parser->yychar =parser->readlineBuffer[0];
      }else
      if (parser->readlineBuffer[ ++parser->readlineWhere - 1] == '\0')
	{
	  free (parser->readlineBuffer);
			
	  parser->readlineWhere = 0;
	  parser->yychar = '\n';
	}else
	parser->yychar = parser->readlineBuffer[ parser->readlineWhere - 1];
  }else
    parser->yychar = fgetc (parser->yyin);
}

PARSER * NewParser(char * file)
{
  PARSER * temp = (PARSER *) malloc (sizeof(PARSER));
  bzero(temp, sizeof(PARSER));
  temp->name = (char *) malloc (sizeof(char) * MAX_STRING_SIZE);
  temp->buffer = (char *) malloc (sizeof(char) * MAX_STRING_SIZE);
  temp->readlineWhere = 0;
  bzero(temp->name, sizeof(char) * MAX_STRING_SIZE);
  bzero(temp->buffer, sizeof(char) * MAX_STRING_SIZE);
  strcpy(temp->name, "<<stdin>>");
  temp->readlineStart = 0;
  OpenFile(file, temp);
  temp->state = 0;
  temp->number = 0;
  temp->line = 0;
  temp->mode = NORMAL;
  return temp;
}

int FetchNumber(PARSER * get)
{
  return get->number;
}

int FetchLine(PARSER * get)
{
  return get->line;
}

void DeleteParser(PARSER * get)
{
  free(get->name);
  /*free(get->buffer);*/
  free(get);
}

static void AppendChar(int a, PARSER * get)
{
  int length = strlen(get->buffer);
  if(length  >= 1023)
    {
      yyerror("name with more than 1024 characters");
    }else{
    get->buffer[length] = a;
    get->buffer[length + 1] = '\0';
  }
}

static void OpenFile (char * name, PARSER * get)
{
	
  strcpy(get->name, name);
	
  if(!(get->yyin = fopen(get->name, "r")))
    {
      strcpy(get->name, "<<stdin>>");
      get->yyin = stdin;
    }
}

static int isGama(int a)
{
  if( (a >= 'A' && a <= 'Z') ||
      (a >= 'a' && a <= 'z') ||
      (a >= '0' && a <= '9') ||
      a == '_' || a == '\\' ||
      a == '-' || a == '<'  ||
      a == '>' || a == '='  ||
      a == '+' || a == '*'  ||
      a == '/' || a == '%'  ||
      a == ',' || a == '{'  ||
      a == '}' || a == ':'  ||
      a == '?' || a == '!')
    return 1;
  else
    return 0;
}

static int isDelta (int a)
{
  if (a >= '0' && a <= '9')
    return 1;
  else
    return 0;
}

static int isWhite(int a)
{
  if(a == ' ' || a == '\t')
    return 1;
  else
    return 0;
}

static int isNewLine(int a)
{
  if(a == '\n')
    return 1;
  else
    return 0;
}	


#define DEBUG(k)       printf("swithing from %d, to %d, by %d\n", parse->state,k, parse->yychar)
#define NEXTCHAR       if(parse->mode !=DONT_FETCH && parse->mode != DONT_DEBUF_AND_FETCH){ readlineFgetc(parse); parse->number++;}else parse->mode = NORMAL
#define NEXTSTATE      break
#define IS(k)          parse->yychar == k

/* Nunjitsu Kung Fu!.... */
#ifdef  DEBUG_LEXIAL_STATE
#define GOTO(k)        DEBUG(k); AppendChar(parse->yychar,parse); parse->state = k
#define JUMP(k)        DEBUG(k); parse->state = k
#else
#define GOTO(k)        AppendChar(parse->yychar,parse); parse->state = k
#define JUMP(k)        parse->state = k
#endif


#define SWITCH(a)      parse->mode = a
#define END_OF_FILE    parse->mode = EOF_F
#define ISNEOF         if(parse->mode != EOF_P)
#define CLEAN_BUFFER   if(parse->mode != DONT_DEBUF && parse->mode != DONT_DEBUF_AND_FETCH) bzero(parse->buffer, sizeof(char) * MAX_STRING_SIZE)
#define APPEND         AppendChar(parse->yychar,parse);
#define ACCEPTED(x)    JUMP(0); return x

int parser(PARSER * parse)
{
  CLEAN_BUFFER;
  ISNEOF{
    while(1)
      {
	NEXTCHAR;
	switch(parse->state)
	  {
	  case 0:
	    if(isWhite(parse->yychar))                    {NEXTSTATE;}else
	      if(isNewLine(parse->yychar))                  {parse->number = 0; parse->line++; NEXTSTATE;}else
		if(IS('n') || IS('N'))                         {GOTO(31);NEXTSTATE;}else
		  if(IS('t') || IS('T'))                         {GOTO(41);NEXTSTATE;}else
		    /* todo:             WHAT THE FUCK!!??!!?? */
		    if(IS('(') || IS(')'))                         {return parse->yychar;}else
		      if(IS(-1))                                     {return EOF_F;}else
			/* atention with # must add all character and shit */
			if(IS('#'))                                    {return parse->yychar;}else
			  if(IS('/'))                                    {GOTO(22);NEXTSTATE;}else
			    if(isDelta(parse->yychar))                     {GOTO(2);NEXTSTATE;}else 
			      if(IS('\''))								   {return '\'';}else
				if(IS('$'))								   {JUMP(500); NEXTSTATE;}else
				  if(IS('.'))                                    {GOTO(93);NEXTSTATE;}else
				    if(isGama(parse->yychar))                      {GOTO(1);NEXTSTATE;}else 
				      if(IS('\"'))                                   {JUMP(10);NEXTSTATE;}else
			
					{yyerror("unexpected character");
					  printf("%d\n", parse->yychar);}break;
	  case 1:
	    if(isGama(parse->yychar))            {GOTO(1);NEXTSTATE;}else
			
	      {SWITCH(DONT_FETCH);ACCEPTED(TOKEN);}
	  case 2:
	    if(isDelta(parse->yychar))           {GOTO(2);NEXTSTATE;}else
	      if(IS('e'))							 {GOTO(5);NEXTSTATE;}else
		if(isGama(parse->yychar))            {GOTO(1);NEXTSTATE;}else
		  if(IS('.'))							 {GOTO(3);NEXTSTATE;}else
			
		    {SWITCH(DONT_FETCH); ACCEPTED(INTEIRO);}
	    break;
	  case 3:
	    if(isDelta(parse->yychar))			 {GOTO(4);NEXTSTATE;}else
	      if(IS('e'))							 {GOTO(5);NEXTSTATE;}else
		{SWITCH(DONT_FETCH); ACCEPTED(REAL);}
	    break;
	  case 4:
	    if(isDelta(parse->yychar))			 {GOTO(4); NEXTSTATE;}else
	      if(IS('e') || IS('E'))				 {GOTO(5); NEXTSTATE;}else
			
		{SWITCH(DONT_FETCH); ACCEPTED(REAL);}
	  case 5:
	    if(isDelta(parse->yychar))           {GOTO(7); NEXTSTATE;}else
	      if(IS('+') || IS('-'))				 {GOTO(6); NEXTSTATE;}else
			
		{yyerror("missing float exponent or wrong sign"); JUMP(0); NEXTSTATE;}
	  case 6:
	    if(isDelta(parse->yychar))           {GOTO(7); NEXTSTATE;}else
			
	      {yyerror("missing float exponent after sign"); JUMP(0); NEXTSTATE;}
	  case 7:
	    if(isDelta(parse->yychar))           {GOTO(7); NEXTSTATE;}else
			
	      {SWITCH(DONT_FETCH); ACCEPTED(REAL);}
	  case 31:
	    if(IS('i') || IS('I'))   {GOTO(32);NEXTSTATE;}else
	      if(isGama(parse->yychar))            {GOTO(1); NEXTSTATE;}else
			
		{SWITCH(DONT_FETCH);ACCEPTED(TOKEN);}
	  case 32:
	    if(IS('L') || IS('l'))   {GOTO(33);NEXTSTATE;}else
	      if(isGama(parse->yychar))            {GOTO(1); NEXTSTATE;}else
			
		{SWITCH(DONT_FETCH);ACCEPTED(TOKEN);}
	  case 33:
	    if(isGama(parse->yychar))            {GOTO(33);NEXTSTATE;}else
			
	      {SWITCH(DONT_FETCH); ACCEPTED(NIL);}
	  case 41:
	    if(isGama(parse->yychar))            {GOTO(1);NEXTSTATE;}else
			
	      {SWITCH(DONT_FETCH); ACCEPTED(T);}
	  case 10:
	    if(IS('\\'))             {GOTO(10);NEXTCHAR;GOTO(10);JUMP(10);NEXTSTATE;}else
	      if(IS('\"'))             {JUMP(10);ACCEPTED(STRING);}else
		if(IS(-1))               {yyerror("unexpected end of file inside of string");return 0;}else
			
		  {GOTO(10);NEXTSTATE;}
	  case 22:
	    if(IS('/'))              {JUMP(23);NEXTSTATE;}else
	      if(IS('*'))              {JUMP(24);NEXTSTATE;}else
		if(isGama(parse->yychar)) {GOTO(1);NEXTSTATE;}else
		  {ACCEPTED(TOKEN);}
	  case 23:
	    if(!isNewLine(parse->yychar))        {JUMP(23);NEXTSTATE;}else
				
	      {JUMP(0); NEXTSTATE;}
	  case 24:
	    if(IS('*'))              {NEXTCHAR;if(IS('/')){JUMP(0);NEXTSTATE;}else{JUMP(24);NEXTSTATE;}}else
			
	      {JUMP(24);NEXTSTATE;}
	  case 93:
	    if(IS(' '))               {GOTO(0);ACCEPTED('.');}
	    if(isDelta(parse->yychar))            {GOTO(3);NEXTSTATE;}else
	      if(IS('.'))							  {GOTO(200); NEXTSTATE;}else
			
		return '.';
	  case 200:
	    if(IS('.'))                          {GOTO(0);ACCEPTED(TOKEN);}
			
	    {yyerror("wired token, revision advised"); GOTO(0); NEXTSTATE;}
	  case 500:
	    if(IS('\\'))
	      {
		NEXTCHAR;
		switch(parse->yychar)
		  {
		  case 'n':
		    parse->yychar = '\n';
		    break;
		  case '"':
		    parse->yychar = '"';
		    break;
		  case '\t':
		    parse->yychar = '\t';
		    break;
		  case '\r':
		    parse->yychar = '\r';
		    break;
		  case '\\':
		    parse->yychar = '\\';
		    break;
		  default:
		    break;
		  }
	      }else
	      if(IS('$'))
		{yyerror("empty character infocation");}
	    GOTO(0);
	    return CHAR;

	  default:
	    printf("error inside parsing algorithm at state %d\n", parse->state);
	    printf("readed %s, got character %d\n",parse->buffer, parse->yychar);
	  }
      }
  }
  return -1;
}
